/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package DTD.Validador;

import DTD.Box;
import DTD.Diccionario.Diccionario;
import DTD.Diccionario.IDiccionario;
import Utilidades.Balance;
import Utilidades.Cola;
import Utilidades.StrOp;
import Utilidades.VerificadorStrings;

/**
 *
 * @author Administrador
 */
public class ValidadorElement {
    public static int SOLO_UNO = 0;
    public static int EMPTY = 1;
    public static int ANY = 2;
    public static int NINGUNA = 3;
    private int cardinalidadElement; //0123
    public Cola<Box.BoxContenido> boxContent;
    private String[] reglas;
    private String opcion; // ? * +
    private Diccionario diccionario;
    private int cantidad;

    public ValidadorElement(final Box box, final String dtdRegla) {
        this.diccionario = new Diccionario();
        this.setOpcional(dtdRegla);
        this.setCardinalidadElemento(dtdRegla);
        this.setReglas(dtdRegla);
        this.setContent(box);

        if (this.reglas == null) {
            cantidad = 0;
        } else {
            cantidad = this.boxContent.size() / this.reglas.length;
        }
    }

    private void setOpcional(final String dtdRegla) {
        String listaSubElementos = StrOp.separar(dtdRegla, " ", "<!ELEMENT", ">", '(', ')')[1];
        this.opcion = listaSubElementos.substring(listaSubElementos.length() - 1);

        if (diccionario.getTipoDiccionario(this.opcion) != IDiccionario.SIMBOLO) {
            this.opcion = null;
        }
    }

    private void setCardinalidadElemento(final String dtdRegla) {
        int idx = dtdRegla.replaceAll(">", " ").split(" ").length - 1;
        String cardinalidad = dtdRegla.replaceAll(">", " ").split(" ")[idx];

        if (cardinalidad.equals("ANY")) {
            this.cardinalidadElement = ValidadorElement.ANY;
        }
        else if (cardinalidad.equals("EMPTY")) {
            this.cardinalidadElement = ValidadorElement.EMPTY;
        }
        else {
            this.cardinalidadElement = ValidadorElement.SOLO_UNO;
        }
    }

    private void setContent(final Box box) {
        this.boxContent = new Cola<Box.BoxContenido>();
        Box.BoxContenido[] cnt = box.getElements();

        for (int i = 0; i < cnt.length; i++) {
            this.boxContent.add(cnt[i]);
        }
    }

    private void setReglas(final String dtdRegla) {
        String lista = StrOp.separar(dtdRegla, " ", "<!ELEMENT", ">", '(', ')')[1];

        if (this.opcion != null) {
            lista = lista.substring(0, lista.length() - 1);
        }

        if (lista.indexOf('(') != -1 && lista.indexOf(')') != -1) {
            this.reglas = StrOp.separar(lista, ",", "(", ")", '(', ')');
        }
        else {
            this.reglas = null;
        }
    }

    private boolean validarCantidad() {
        boolean resultado = true;

        if (cantidad != 0 || this.cardinalidadElement != ValidadorElement.EMPTY) {
            if (reglas != null && cantidad * this.reglas.length != this.boxContent.size()) {
                resultado = false;
            }
            else {
                if (opcion == null) {
                    resultado = (cantidad == 1);
                }
                else if (opcion.equals("*")) {
                    resultado = (cantidad >= 0);
                }
                else if (opcion.equals("?")) {
                    resultado = (cantidad == 0 || cantidad == 1);
                }
                else if (opcion.equals("+")) {
                    resultado = (cantidad >= 1);
                }
            }
        }
        return resultado;
    }

    @SuppressWarnings("unchecked")
    public boolean validar() {
        boolean resultado = true;

        if (cardinalidadElement != ValidadorElement.ANY || (cardinalidadElement != ValidadorElement.EMPTY && boxContent.isEmpty())) {
            if (validarCantidad() == false) {
                resultado = false;
            }
            else {
                Cola<Box.BoxContenido>[] grupo = new Cola[cantidad];
                for (int i = 0; resultado == true && i < grupo.length; i++) {
                    grupo[i] = new Cola<Box.BoxContenido>();
                    for (int j = 0; j < reglas.length; j++) {
                        grupo[i].push(boxContent.pop());
                    }
                    resultado = validarGrupo(grupo[i]);
                }
            }
        }
        return resultado;
    }

    private boolean validarGrupo(final Cola<Box.BoxContenido> subGrupo) {
        boolean resultado = true;

        for (int i = 0; i < reglas.length && resultado; i++) {
            String regla = reglas[i];
            Box.BoxContenido aux = subGrupo.pop();
            resultado = validarRegla(regla, aux);
        }
        return resultado;
    }

    private boolean validarRegla(final String regla, final Box.BoxContenido aux) {
        boolean resultado = true;
        String[] rDescompuesta = regla.replaceAll("[(]", " ").replaceAll("[)]", " ").trim().split("[|]");

        if (rDescompuesta[0].trim().equals("#PCDATA")) {
            if (validarPCDATA(aux) == false) {
                resultado = false;
            }
        }
        else if (resultado == true) {
            int i = 0;
            if (rDescompuesta[0].trim().equals("#PCDATA")) {
                i++; //corre uno el indice si ya utilizo #PCDATA
            }
            for (; i < rDescompuesta.length; i++) {
                resultado = resultado || (rDescompuesta[i].equals(aux.getContenido()) && aux.getTipoDato() == Box.ELEMENT);
            }
        }
        return resultado;
    }

    private boolean validarPCDATA(final Box.BoxContenido aux) {
        boolean resultado = true;
        String contenido = aux.getContenido();

        if (contenido.indexOf('"') != -1) {
            if (Balance.esBalanceada(contenido, '"', '"')) {
                contenido = contenido.replace('"', ' ').trim();
            }
            else {
                resultado = false;
            }
        }

        if (resultado && aux.getTipoDato() != Box.CONTENT) {
            resultado = false;
        }
        else if (resultado && VerificadorStrings.soloTextoGuionesYEspacios(contenido.replace('@', '_').replace('.', '_')) == false) {
            resultado = false;
        }
        return resultado;
    }
}
